package com.sdhs.common.mybatis;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Properties;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.util.FieldUtils;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;

/**
 * 为无@TableName注解的实体类自动生成id，以最小的改动适配若依代码
 * @author sunningzhen
 */
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class PopulateTableIdInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(PopulateTableIdInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        if (SqlCommandType.INSERT.name().equals(ms.getSqlCommandType().name())) {
            ParameterMap parameterMap = ms.getParameterMap();
            Class<?> type = parameterMap.getType();
            if (type != null) {
                Field[] fields = type.getDeclaredFields();
                Object entity = args[1];
                for (Field field : fields) {
                    TableId annotation = field.getAnnotation(TableId.class);
                    if (annotation != null) {
                        Object value = FieldUtils.getFieldValue(entity, field.getName());
                        if (value == null) {
                            IdentifierGenerator identifierGenerator = GlobalConfigUtils
                                    .getGlobalConfig(ms.getConfiguration()).getIdentifierGenerator();
                            if (annotation.type() == IdType.ASSIGN_ID) {
                                Number id = identifierGenerator.nextId(entity);
                                if (Integer.class.isAssignableFrom(field.getType())) {
                                    FieldUtils.setProtectedFieldValue(field.getName(), entity, id.intValue());
                                } else if (Long.class.isAssignableFrom(field.getType())) {
                                    FieldUtils.setProtectedFieldValue(field.getName(), entity, id.longValue());
                                } else if (BigDecimal.class.isAssignableFrom(field.getType())) {
                                    FieldUtils.setProtectedFieldValue(field.getName(), entity,
                                                                      new BigDecimal(id.longValue()));
                                } else if (BigInteger.class.isAssignableFrom(field.getType())) {
                                    FieldUtils.setProtectedFieldValue(field.getName(), entity,
                                                                      new BigInteger(id.toString()));
                                } else if (String.class.isAssignableFrom(field.getType())) {
                                    FieldUtils.setProtectedFieldValue(field.getName(), entity, id.toString());
                                } else {
                                    throw new MybatisPlusException(
                                            "Field type '" + field.getType().getSimpleName() + "' not supported");
                                }
                            } else if ((annotation.type() == IdType.ASSIGN_UUID)) {
                                FieldUtils.setProtectedFieldValue(field.getName(), entity,
                                                                  identifierGenerator.nextUUID(entity));
                            }
                        }
                    }
                }
            }
        }
        return invocation.proceed();
    }

    @Override
    public void setProperties(Properties properties) {
        logger.info(properties.toString());
    }
}
